module cacheControlIns(
  input                             clk              ,
  input                             clk_50M          ,
  input                             sdram_clk        ,
  input                             sys_rst_n        ,

  input   [23:0]     avs_s0_address     ,     //    s0.address//不包括低两位
  input              avs_s0_read        ,        //      .read
  output  [31:0]     avs_s0_readdata    ,    //      .readdata
  output             avs_s0_waitrequest , //      .waitrequest

  output    reg read_ins_cache_req        ,
  input         read_ins_cache_ack        ,

//  input             control_by_sdram            ,
  input [7:0]       sdram_cache_address         ,
  input             sdram_cache_write_enable    ,
  input [31:0]      sdram_cacheData             ,
  input [3:0]       sdram_cache_byteena         ,
  output     [24:0] rdwr_sdram_addr             ,

  output reg [24:0]            glich_count,
  input                        refresh_ins,
  
  output                [15:0] cache_config,
  
  input                  [4:0] cache_debug_index,
  output            reg [31:0] cache_debug_data

);

//`define singleOutIns singleOut2
//localparam GROUP_COUNT = 2,GROUP_WIDTH = 1,CACHE_COUNT = 2,CACHE_COUNT_BIT = 1'h1,CACHE_LIFE_WIDTH = 1;`define cache_type_ins cache32x1k// 2*2=4k
//localparam GROUP_COUNT = 4,GROUP_WIDTH = 2,CACHE_COUNT = 2,CACHE_COUNT_BIT = 1'h1,CACHE_LIFE_WIDTH = 1;// 4*2=8k
//localparam GROUP_COUNT = 8,GROUP_WIDTH = 3,CACHE_COUNT = 2,CACHE_COUNT_BIT = 1'h1,CACHE_LIFE_WIDTH = 1;// 8*2=16k
//localparam GROUP_COUNT =16,GROUP_WIDTH = 4,CACHE_COUNT = 2,CACHE_COUNT_BIT = 1'h1,CACHE_LIFE_WIDTH = 1;`define cache_type_ins cache32x8k//16*2=32k

`define singleOutIns singleOut4
//localparam GROUP_COUNT = 2,GROUP_WIDTH = 1,CACHE_COUNT = 4,CACHE_COUNT_BIT = 2'h3,CACHE_LIFE_WIDTH = 2;`define cache_type cache32x2k// 2*4=8k
//localparam GROUP_COUNT = 4,GROUP_WIDTH = 2,CACHE_COUNT = 4,CACHE_COUNT_BIT = 2'h3,CACHE_LIFE_WIDTH = 2;// 4*4=16k
localparam GROUP_COUNT = 8,GROUP_WIDTH = 3,CACHE_COUNT = 4,CACHE_COUNT_BIT = 2'h3,CACHE_LIFE_WIDTH = 2;`define cache_type_ins cache32x8k// 8*4=32k

//`define singleOutIns singleOut8
//localparam GROUP_COUNT = 2,GROUP_WIDTH = 1,CACHE_COUNT = 8,CACHE_COUNT_BIT = 3'h7,CACHE_LIFE_WIDTH = 3;//2*8=16k
//localparam GROUP_COUNT = 4,GROUP_WIDTH = 2,CACHE_COUNT = 8,CACHE_COUNT_BIT = 3'h7,CACHE_LIFE_WIDTH = 3;`define cache_type_ins cache32x8k//4*8=32k

//`define singleOutIns singleOut16
//localparam GROUP_COUNT = 2,GROUP_WIDTH = 1,CACHE_COUNT = 16,CACHE_COUNT_BIT = 4'hF,CACHE_LIFE_WIDTH = 4;`define cache_type_ins cache32x8k//2*16=32k

//4*10 = 40k

assign cache_config[7:0] = CACHE_COUNT[7:0];
assign cache_config[15:8] = GROUP_COUNT[7:0];

`define index_cache cache_debug_index[CACHE_LIFE_WIDTH+GROUP_WIDTH-1:GROUP_WIDTH]
`define index_group cache_debug_index[GROUP_WIDTH-1:0]

always@(posedge clk_50M or negedge sys_rst_n) begin
  if(!sys_rst_n) begin
    cache_debug_data <= 0;
  end else begin
    cache_debug_data[31] <=                                             1'b0;
    cache_debug_data[30] <=     cacheInvalid[ `index_cache ][ `index_group ];
    cache_debug_data[29:16] <=    cache_life[ `index_cache ][ `index_group ];
    cache_debug_data[15:0] <= {cacheAddrHigh[ `index_cache ][ `index_group ], `index_group};
  end
end

wire [GROUP_WIDTH-1:0] curr_group_id = avs_s0_address[8+GROUP_WIDTH-1:8];

wire                      cacheInvalid[0:CACHE_COUNT-1][0:GROUP_COUNT-1];
wire [15-GROUP_WIDTH:0]  cacheAddrHigh[0:CACHE_COUNT-1][0:GROUP_COUNT-1];
wire [CACHE_LIFE_WIDTH-1:0] cache_life[0:CACHE_COUNT-1][0:GROUP_COUNT-1];

wire [CACHE_COUNT-1:0] free_cache_temp;
wire [CACHE_COUNT-1:0] cacheInvalid_temp;
wire [CACHE_COUNT-1:0] cache_hited;


genvar i;
genvar j;
generate
  for(j=0; j<GROUP_COUNT; j=j+1) begin:BLOCK2

    wire latched_group_selected = latched_group_id == j;
    
    for(i=0; i<CACHE_COUNT; i=i+1) begin:BLOCK1
      cacheEntityIns #(
        .GROUP_WIDTH       (GROUP_WIDTH),
        .CACHE_COUNT       (CACHE_COUNT),
        .CACHE_COUNT_BIT   (CACHE_COUNT_BIT),
        .CACHE_LIFE_WIDTH  (CACHE_LIFE_WIDTH)
        )
        cacheEntityIns_inst(
        .clk                (clk                     ),
        .sys_rst_n          (sys_rst_n               ),
        .latched_group_selected(latched_group_selected),
        .current_slot       (current_slot[i]         ),
        .set_cacheAddrHigh  (set_cacheAddrHigh       ),
        .avs_s0_address     (avs_s0_address          ),
        .adj_cache_life     (adj_cache_life          ),
        .curr_cache_life    (curr_cache_life         ),
        
        .refresh_ins        (refresh_ins             ),
        .cacheInvalid       (cacheInvalid[i][j]      ),
        .cacheAddrHigh      (cacheAddrHigh[i][j]     ),
        .cache_life         (cache_life[i][j]        ) 
      );
    end

  end
  for(i=0; i<CACHE_COUNT; i=i+1) begin:BLOCK0
    assign cache_hited[i]       = cacheInvalid[i][curr_group_id] == 0 && avs_s0_address[23:8+GROUP_WIDTH] == cacheAddrHigh[i][curr_group_id];
    assign free_cache_temp[i]   = cache_life[i][curr_group_id] == 0;
    assign cacheInvalid_temp[i] = cacheInvalid[i][curr_group_id];
  end
endgenerate

  reg glich;
  always@(posedge clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
      glich <= 0;
      glich_count <= 0;
    end else begin
      glich <= free_cache_temp == 0;
      if(glich)begin
        glich_count <= glich_count + 1'b1;
      end
    end
  end


  wire [CACHE_LIFE_WIDTH-1:0] cache_hited_addr;
  `singleOutIns ins_singleOut4(
  .in(cache_hited),
  .addr(cache_hited_addr)
  );
  

  wire cache_write_enable     = sdram_cache_write_enable;//control_by_sdram ?   : 1'b0
  wire [31:0] cache_cacheData = sdram_cacheData;
  wire [3:0] cache_byteena    = sdram_cache_byteena;

  //wire [31:0] cacheq;
  wire [31:0] cache_hit_data;//        = cacheq;//cacheq[cache_hited_addr][curr_group_id];
  //wire [31:0] cache_curr_data;//       = cacheq;//cacheq[current_slot_addr][curr_group_id];
//32*256*GROUP_COUNT*CACHE_COUNT
`ifdef isALTERA

  altsyncram	cacheArray (
    .clock0 (sdram_clk),
    .address_a ({curr_group_id, current_slot_addr, sdram_cache_address[7:0]}),
    .byteena_a (cache_byteena),
    .data_a (cache_cacheData),
    .wren_a (cache_write_enable),

    .clock1 (clk),
    .address_b ({curr_group_id, cache_hited_addr, avs_s0_address[7:0]}),
    .q_b (cache_hit_data),

    .aclr0 (1'b0),
    .aclr1 (1'b0),
    .addressstall_a (1'b0),
    .addressstall_b (1'b0),
    .byteena_b (1'b1),
    .clocken0 (1'b1),
    .clocken1 (1'b1),
    .clocken2 (1'b1),
    .clocken3 (1'b1),
    .data_b ({32{1'b1}}),
    .eccstatus (),
    .q_a (),
    .rden_a (1'b1),
    .rden_b (1'b1),
    .wren_b (1'b0));
  defparam
    cacheArray.address_aclr_b = "NONE",
    cacheArray.address_reg_b = "CLOCK1",
    cacheArray.byte_size = 8,
    cacheArray.clock_enable_input_a = "BYPASS",
    cacheArray.clock_enable_input_b = "BYPASS",
    cacheArray.clock_enable_output_b = "BYPASS",
    cacheArray.intended_device_family = "Cyclone IV E",
    cacheArray.lpm_type = "altsyncram",
    cacheArray.numwords_a = 256*GROUP_COUNT*CACHE_COUNT,
    cacheArray.numwords_b = 256*GROUP_COUNT*CACHE_COUNT,
    cacheArray.operation_mode = "DUAL_PORT",
    cacheArray.outdata_aclr_b = "NONE",
    cacheArray.outdata_reg_b = "UNREGISTERED",
    cacheArray.power_up_uninitialized = "FALSE",
    cacheArray.widthad_a = 8+GROUP_WIDTH+CACHE_LIFE_WIDTH,
    cacheArray.widthad_b = 8+GROUP_WIDTH+CACHE_LIFE_WIDTH,
    cacheArray.width_a = 32,
    cacheArray.width_b = 32,
    cacheArray.width_byteena_a = 4;
`endif

`ifdef isVIVADO
  `cache_type_ins cache_ins(
    .clka   (cache_clk),
    .wea    (cache_byteena & {(4){cache_write_enable}}),
    .addra  ({curr_group_id, current_slot_addr, cacheAddrLow8}),
    .dina   (cache_cacheData),
    .douta  (cache_curr_data),
    .clkb   (cache_clk),
    .web    (0),
    .addrb  ({curr_group_id, cache_hited_addr, cacheAddrLow8}),
    .dinb   (0),
    .doutb  (cache_hit_data)
  );
`endif

    
reg set_cacheAddrHigh;
reg [CACHE_COUNT-1:0] current_slot;

wire [CACHE_COUNT-1:0] free_cache;
`singleOutIns ins_singleOut1(
  .in(free_cache_temp),//[curr_group_id]
  .out(free_cache),
  .def(1)
);

wire [CACHE_COUNT-1:0] cacheInvalidSingle;
`singleOutIns ins_singleOut2(
  .in(cacheInvalid_temp),//[curr_group_id]
  .out(cacheInvalidSingle),
  .def(0)
);


wire [CACHE_LIFE_WIDTH-1:0] current_slot_addr;
`singleOutIns ins_singleOut3(
  .in(current_slot),
  .addr(current_slot_addr)
);

wire [15-GROUP_WIDTH:0] current_cacheAddrHigh = cacheAddrHigh[current_slot_addr][curr_group_id];
wire [CACHE_LIFE_WIDTH-1:0]   curr_cache_life = cache_life[current_slot_addr][curr_group_id];//glich ? {CACHE_LIFE_WIDTH{1'b0}} : 


reg [1:0]  interface_status;
localparam STATUS_INIT              = 0;
localparam STATUS_READ_CACHE        = 1;
localparam STATUS_READ_CACHE2       = 2;
localparam STATUS_STEP2             = 3;

`define FINISH_INS interface_status <= STATUS_INIT;avs_s0_read_ack <= 1;

reg adj_cache_life;//更新：  1，命中时，

reg [GROUP_WIDTH-1:0] latched_group_id;

reg avs_s0_read_ack;

reg read_ins_cache_ack_buff;

assign avs_s0_waitrequest = avs_s0_read && !avs_s0_read_ack;

assign avs_s0_readdata = cache_hit_data;

assign rdwr_sdram_addr = {current_cacheAddrHigh,curr_group_id,8'b0,1'b0};

always@(posedge clk or negedge sys_rst_n) begin
  if(!sys_rst_n) begin
    avs_s0_read_ack <= 0;
    //avs_s0_readdataReg <= 0;
    interface_status <= 0;
    //rdwr_sdram_addr <= 0;

    read_ins_cache_req <= 0;

    read_ins_cache_ack_buff <= 0;
    
    current_slot <= 0;
    
    adj_cache_life <= 0;
    set_cacheAddrHigh <= 0;

  end else begin
    read_ins_cache_ack_buff <= read_ins_cache_ack;
    
    avs_s0_read_ack <= 0;

    adj_cache_life <= 0;
    set_cacheAddrHigh <= 0;
    
    case (interface_status)
    STATUS_INIT : begin//初始化
      if(avs_s0_read && !avs_s0_read_ack)begin
        latched_group_id <= curr_group_id;

        if(cache_hited)begin
          //命中
          adj_cache_life <= 1;//wire [CACHE_LIFE_WIDTH-1:0] curr_cache_life =cache_life[current_slot_addr];
          current_slot <= cache_hited;
          interface_status <= STATUS_INIT;
          avs_s0_read_ack  <= 1;
        end else begin
          interface_status <= STATUS_STEP2;
        end
      end
    end

    STATUS_STEP2 : begin
      adj_cache_life <= 1;//wire [CACHE_LIFE_WIDTH-1:0] curr_cache_life =cache_life[current_slot_addr];
      if(cacheInvalidSingle)begin
        //有无效cache，读取cache
        current_slot <= cacheInvalidSingle;
        set_cacheAddrHigh <= 1;//当前地址写入缓存地址高
        interface_status <= STATUS_READ_CACHE;
      end else begin//if(ok_to_use_free_cache)
        //有空闲cache，写回后读取cache
        current_slot <= free_cache;
        set_cacheAddrHigh <= 1;
        interface_status <= STATUS_READ_CACHE;
      end
    end

    STATUS_READ_CACHE : begin
      //rdwr_sdram_addr <= {current_cacheAddrHigh,curr_group_id,8'b0,1'b0};
      read_ins_cache_req <= 1;
      interface_status <= STATUS_READ_CACHE2;
    end
    
    STATUS_READ_CACHE2 : begin
      if(read_ins_cache_ack_buff)begin
        read_ins_cache_req <= 0;
        `FINISH_INS
      end
    end

    endcase

  end
end


endmodule

module cacheEntityIns
#(parameter GROUP_WIDTH=1,CACHE_COUNT=2,CACHE_COUNT_BIT=1'h1,CACHE_LIFE_WIDTH=1)
(
  input                             clk              ,
  input                             sys_rst_n        ,
  input                             latched_group_selected ,
  input                             current_slot     ,
  input                             set_cacheAddrHigh,
  input                      [23:0] avs_s0_address   ,
  input                             adj_cache_life   ,
  input      [CACHE_LIFE_WIDTH-1:0] curr_cache_life  ,
  
  input                             refresh_ins      ,
  output reg                        cacheInvalid     ,
  output reg     [15-GROUP_WIDTH:0] cacheAddrHigh    ,
  output reg [CACHE_LIFE_WIDTH-1:0] cache_life       
);

always@(posedge clk or negedge sys_rst_n) begin
  if(!sys_rst_n) begin
    cache_life <= 0;//0;
    cacheAddrHigh <= 0;
    cacheInvalid <= 1'b1;
  end else begin
    if(latched_group_selected)begin
      if(adj_cache_life)begin
        if(current_slot)begin
          cache_life<=CACHE_COUNT_BIT;//15
        end else begin
          if(cache_life > curr_cache_life)begin
            if(cache_life!=0)begin
              cache_life <= cache_life-1'b1;
            end
          end
        end
      end
      
      if(set_cacheAddrHigh && current_slot)begin
        cacheAddrHigh <= avs_s0_address[23:8+GROUP_WIDTH];
        cacheInvalid <= 1'b0;
      end
    end
    if(refresh_ins)begin
      cacheInvalid <= 1'b1;
    end
  end
end

endmodule 
